package cn.xyfmzmy.spring;

import java.io.File;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URL;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 自定义ApplicationContext容器
 * 负责造对象和拿对象
 */
public class ApplicationContext {

    // 存放对象的容器,IOC本体
    private Map<String,Object> ioc = new HashMap<>();

    // 存放还未初始化完的bean,用于解决循环依赖问题
    private Map<String,Object> loadingIoc = new HashMap<>();

    // BeanDefinition集合，先于ioc填充元素
    private Map<String,BeanDefinition> beanDefinitionMap = new HashMap<>();

    // 存放实现了BeanPostProcessor接口(定义了生命周期函数)的bean
    private List<BeanPostProcessor> postProcessors = new ArrayList<>();

    // 改造函数，需要传入扫描包名
    public ApplicationContext(String packageName) throws Exception {
        initContext(packageName);
    }

    // 在上下文对象初始化的时候造对象
    public void initContext(String packageName) throws Exception {
        // 先创建好所有的BeanDefinition，再创建Bean，这样获取Bean时就能区分究竟是没有还是还没创建
        scanPackage(packageName).stream()
                .filter(this::canCreate)       // 过滤掉不包含指定注解的
                .forEach(this::wrapper);        // Class -> BeanDefinition
        initBeanPostProcessor();                // 初始化List<BeanPostProcessor>
        beanDefinitionMap.values().stream().forEach(this::createBean);     // 根据BeanDefinition创建Bean

    }

    // 初始化List<BeanPostProcessor>
    private void initBeanPostProcessor() {
        beanDefinitionMap.values().stream()
                .filter(bd -> BeanPostProcessor.class.isAssignableFrom(bd.getBeanType()))   // 过滤掉没有实现BeanPostProcessor的类
                .map(this::createBean)                      // 根据BeanDefinition创建Bean
                .map((bean) -> (BeanPostProcessor) bean)    // 强转为BeanPostProcessor
                .forEach(postProcessors::add);              // 加入postProcessors中
    }

    // 根据BeanDefinition创造Bean对象
    protected Object createBean(BeanDefinition beanDefinition){
        String name = beanDefinition.getName();
        if(ioc.containsKey(name)){
            // 容器中已存在该对象就直接返回
            return ioc.get(name);
        }
        if(loadingIoc.containsKey(name)){
            // loadingIoc中存在也直接返回,解决循环依赖问题
            return loadingIoc.get(name);
        }
        // 利用beanDefinition里的构造函数创造bean对象，并放入ioc容器中
        Constructor<?> constructor = beanDefinition.getConstructor();
        Object bean = null;
        try {
            bean = constructor.newInstance();
            // 放入loadingIoc中,用于解决循环依赖导致栈溢出
            loadingIoc.put(beanDefinition.getName(),bean);
            // 给bean对象注入Autowired修饰的属性
            autowiredBean(bean,beanDefinition);
            // 初始化bean
            bean = initializeBean(bean,beanDefinition);
            // 初始化完成，放入真正的ioc容器
            loadingIoc.remove(beanDefinition.getName());
            ioc.put(beanDefinition.getName(),bean);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }

        return bean;
    }

    // 初始化bean
    private Object initializeBean(Object bean, BeanDefinition beanDefinition) throws Exception {
        // 执行beforeInitializeBean
        for(BeanPostProcessor postProcessor:postProcessors){
            bean = postProcessor.beforeInitializeBean(bean,beanDefinition.getName());
        }

        // 判断该bean对象是否有PostConstruct函数,有就执行
        Method postConstructMethod = beanDefinition.getPostConstructMethod();
        if(postConstructMethod != null){
            postConstructMethod.invoke(bean);
        }

        // 执行afterInitializeBean
        for(BeanPostProcessor postProcessor:postProcessors){
            bean = postProcessor.afterInitializeBean(bean,beanDefinition.getName());
        }

        return bean;
    }

    // 给bean对象注入@Autowired修饰的属性
    private void autowiredBean(Object bean, BeanDefinition beanDefinition) throws IllegalAccessException {
        // 遍历被@Autowired修饰的属性
        for (Field autowiredField : beanDefinition.getAutowiredFields()) {
            autowiredField.setAccessible(true);
            autowiredField.set(bean,getBean(autowiredField.getType()));     // 获取bean并赋值
        }
    }

    // class type -> BeanDefinition
    protected BeanDefinition wrapper(Class<?> type){
        BeanDefinition beanDefinition = new BeanDefinition(type);
        // 解决bean名字重复的问题
        if (beanDefinitionMap.containsKey(beanDefinition.getName())){
            throw new RuntimeException("bean名字重复");
        }
        beanDefinitionMap.put(beanDefinition.getName(),beanDefinition);
        return beanDefinition;
    }

    // 判断是否包含所需的注解，支持重写自定义
    protected boolean canCreate(Class<?> type){
        return type.isAnnotationPresent(Componet.class);
    }

    // 扫描包名下的类，返回Class集合
    private List<Class<?>> scanPackage(String packageName) throws Exception {
        // 传入的包名如 a.b.c
        List<Class<?>> classList = new ArrayList<>();
        URL resource = this.getClass().getClassLoader().getResource(packageName.replace(".", File.separator));
        Path path = Path.of(resource.getFile());
        // 递归地遍历该目录下的文件
        Files.walkFileTree(path,new SimpleFileVisitor<>(){
            @Override
            public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
                // 这里定义访问逻辑
                Path absolutePath = file.toAbsolutePath();
                if(absolutePath.toString().endsWith(".class")){
                    String replaceStr = absolutePath.toString().replace(File.separator, ".");
                    int packageIndex = replaceStr.indexOf(packageName);
                    String className = replaceStr.substring(packageIndex, replaceStr.length() - ".class".length());
                    try {
                        classList.add(Class.forName(className));
                    } catch (ClassNotFoundException e) {
                        throw new RuntimeException(e);
                    }
                }
                return FileVisitResult.CONTINUE;        // 继续访问
            }
        });
        return classList;
    }

    // 通过名字获取对象
    public Object getBean(String name) {
        if (name == null) {
            return null;
        }
        Object bean = this.ioc.get(name);
        // 不为空，直接返回
        if(bean != null){
            return bean;
        }
        // 为空，进一步判断是还未创建还是压根没有(判断是否有相应的beanDefination)
        if(beanDefinitionMap.containsKey(name)){
            return createBean(beanDefinitionMap.get(name));
        }
        return null;
    }

    // 通过类型获取对象
    public <T> T getBean(Class<T> beanType){
        // todo: 通过类型取获取bean的时候需要把整个ioc容器都遍历一遍，因为无法用class类型取作为ioc的key，这会导致接口和父类无法获取单一的bean
        // 遍历beanDefinitionMap获取相应的BeanDefinition,获取对应beanName,再根据name获取bean
        String beanName = this.beanDefinitionMap.values().stream()
                .filter(bd -> beanType.isAssignableFrom(bd.getBeanType()))      // beanType是否可以复制给bean.getClass(),也就是否为实现/继承关系
                .map(BeanDefinition::getName)
                .findFirst()
                .orElse(null);
        return (T) getBean(beanName);
    }

    public <T> List<T> getBeans(Class<T> beanType){
        return this.beanDefinitionMap.values().stream()
                .filter(bd -> beanType.isAssignableFrom(bd.getBeanType()))      // beanType是否可以复制给bean.getClass(),也就是否为实现/继承关系
                .map(BeanDefinition::getName)
                .map(this::getBean)
                .map((bean) -> (T) bean)
                .toList();
    }

}
